﻿// <copyright file="StateZipTotals.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-06-12</date>
// <summary>StateZipTotals class definition.</summary>

namespace NYTimes
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using NYTimes.Core;
    using NYTimes.Data;

    /// <summary>
    /// Campaign totals resource type
    /// </summary>
    public enum CampaignTotalsResourceType
    {
        /// <summary>
        /// Specify a state
        /// </summary>
        States,
        
        /// <summary>
        /// Specify a zip code
        /// </summary>
        Zips
    }

    /// <summary>
    /// New York Times Campaign Finance State/Zip Totals API
    /// </summary>
    public class StateZipTotals : CampaignFinance
    {
        #region Fields
        /// <summary>
        /// the campaign year
        /// </summary>
        private int year;

        /// <summary>
        /// Resource type
        /// </summary>
        private CampaignTotalsResourceType resourceType;

        /// <summary>
        /// The location (2-letter state or 5-digit zip code)
        /// </summary>
        private string state;

        /// <summary>
        /// Zip code to search
        /// </summary>
        private string zip;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the StateZipTotals class.  
        /// </summary>
        /// <remarks>If this is in design view, load the results with StateZipTotals.xml from the 
        /// NYTimesSampleData assembly.</remarks>
        public StateZipTotals()
        {
            this.Results = new ObservableCollection<CandidateLocationTotal>();

            if (SampleData.IsInDesignTool)
            {
                var xml = NYTimes.Core.SampleData.GetSampleData("StateZipTotals.xml");

                this.ParseXml(xml);
            }
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the year. (currently 2008 is the only available year and is the default
        /// value)
        /// </summary>
        public int Year
        {
            get
            {
                return this.year;
            }

            set
            {
                if (this.year != value)
                {
                    this.year = value;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets or sets the states | zips
        /// </summary>
        public CampaignTotalsResourceType ResourceType
        {
            get
            {
                return this.resourceType;
            }

            set
            {
                if (this.resourceType != value)
                {
                    this.resourceType = value;

                    FetchData();

                    this.TriggerPropertyChanged("ResourceType");
                }
            }
        }

        /// <summary>
        /// Gets or sets the two-letter state abbreviation
        /// </summary>
        public string State
        {
            get
            {
                return this.state;
            }

            set
            {
                if (this.state != value)
                {
                    this.state = value;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets or sets the five-digit ZIP code
        /// </summary>
        public string ZipCode
        {
            get
            {
                return this.zip;
            }

            set
            {
                if (this.zip != value)
                {
                    this.zip = value;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets the candidate totals
        /// </summary>
        public ObservableCollection<CandidateLocationTotal> Results { get; private set; }
        #endregion

        #region Methods

        /// <summary>
        /// Gets a value indicating whether the location is not null or empty
        /// </summary>
        protected override bool CanFetchData
        {
            get
            {
                switch (this.ResourceType)
                {
                    case CampaignTotalsResourceType.States:
                        return !string.IsNullOrEmpty(this.State);

                    case CampaignTotalsResourceType.Zips:
                        return !string.IsNullOrEmpty(this.ZipCode);
                }

                return false;
            }
        }

        /// <summary>
        /// Clear the results
        /// </summary>
        protected override void ClearResults()
        {
            this.Results.Clear();
        }

        /// <summary>
        /// Generate an address for the service
        /// </summary>
        /// <param name="baseAddress">the base address</param>
        /// <returns>an address for the service</returns>
        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", 
            Justification = "Not relevant for Silverlight.")]
        protected override Uri GenerateAddress(string baseAddress)
        {
            var builder = new StringBuilder(baseAddress);

            switch (this.ResourceType)
            {
                case CampaignTotalsResourceType.States:
                    builder.AppendFormat(
                        CultureInfo.InvariantCulture,
                        "{0}/{4}/{1}/{2}.xml?api-key={3}",
                        this.Year,
                        this.ResourceType.ToString().ToLower(CultureInfo.InvariantCulture),
                        this.State.ToUpper(CultureInfo.InvariantCulture),
                        this.ApiKey,
                        this.CampaignType.ToString().ToLower(CultureInfo.InvariantCulture));
                    break;

                case CampaignTotalsResourceType.Zips:
                    builder.AppendFormat(
                        CultureInfo.InvariantCulture,
                        "{0}/{4}/{1}/{2}.xml?api-key={3}",
                        this.Year,
                        this.ResourceType.ToString().ToLower(CultureInfo.InvariantCulture),
                        this.ZipCode,
                        this.ApiKey,
                        this.CampaignType.ToString().ToLower(CultureInfo.InvariantCulture));
                    break;
            }

            return new Uri(builder.ToString(), UriKind.Absolute);
        }

        /// <summary>
        /// Create a result total for each candidate and put it into the Results collection
        /// </summary>
        /// <param name="results">the results element</param>
        /// <returns>alwasy returns true</returns>
        protected override bool ProcessResults(XElement results)
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }

            var candidates = from candidate in results.Elements("candidate")
                             select new CandidateLocationTotal()
                             {
                                 ContributionCount = int.Parse(candidate.Element("contribution_count").Value, CultureInfo.InvariantCulture),
                                 FullName = candidate.Element("full_name").Value,
                                 Party = candidate.Element("party").Value,
                                 State = candidate.Element("state") == null ? null : candidate.Element("state").Value,
                                 Total = double.Parse(candidate.Element("total").Value, CultureInfo.InvariantCulture),
                                 ZipCode = candidate.Element("zip") == null ? null : candidate.Element("zip").Value
                             };

            foreach (var candidate in candidates)
            {
                this.Results.Add(candidate);
            }

            return true;
        }
        #endregion
    }
}
